<?php
class ss_data_jsonquery{
	private $tokens = array();
    private $depth = 0;  
    private $str = array();
	/*
	 * /"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g
	 * "/\"(\\.|[^"\\])*\"|'(\\.|[^'\\])*'|[\[\]]/g"
	 */
	public function query($query,$obj=array()){
		//$tokens = array();
		ss_error::group('Main regexp 1');
    	$query = preg_replace_callback("/'(\\\\.|[^'\\\\])*'|[\[\]]/",array($this,'query_calback_1'),$query);
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		$this->prefix = '';
		
		// change the equals to comparisons
		ss_error::group('Main regexp 2');
    	$query=preg_replace('/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/','error',$query); 
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 3');
		$query = preg_replace('/([^=]=)([^=])/U',"$1=$2",$query);
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 4');
		$query = preg_replace_callback('/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/',array($this,'query_calback_2'),$query);
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 5');
		$query = preg_replace_callback('/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/',array($this,'query_calback_3'),$query);
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 6');
		$query=preg_replace_callback('/(\$obj\s*(\.\s*[\w_$]+\s*)*)(==|~)\s*`([0-9]+)/',array($this,'makeRegex'),$query); // create regex matching
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 7');
		$query=preg_replace_callback('/`([0-9]+)\s*(==|~)\s*(\$obj(\s*\.\s*[\w_$]+)*)/',create_function('$q','return $this->makeRegex($q[0],$q[4],$q[3],$q[2],$q[1]);'),$query); // and do it for reverse =
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		ss_error::group('Main regexp 8');
		$query = $this->prefix . ($query[0] == '$' ? "" : "$") . preg_replace_callback('/`([0-9]+|\],)/',array($this,'query_calback_4'),$query);
		ss_error::log($query,'Result');
		ss_error::groupEnd();
		
    	//$executor = create_function('$,$1,$2,$3,$4,$5,$6,$7,$8,$9','var $obj=$;return ' + $query + "}");
		ss_error::log($this->str,'Strings');
		return $query;
		
	}
	
    function call($name){
    	ss_error::log($name,'Calling');
      // creates a function call and puts the expression so far in a parameter for a call
      $this->prefix = $name . "(" . $this->prefix;
    }
    function makeRegex($q){
    	ss_error::log($q,'Creating regexp from');
    	@list($t,$a,$b,$c,$d) = $q;
		return preg_match('/[\*\?]/',$this->str[$d]) || $c=='~' ?
			"/^".preg_replace('/([\*\?])/','.$1',preg_replace("/\\\\([btnfr\\\\\"'])|([^\w\*\?])/",'\\$1$2',substr($this->str[$d],1,strlen($this->str[$d])-1))). ($c == '~' ? '$/i' : '$/') . ".test(" . $a . ")" :$t;
    }
	function query_calback_1($t){
		$t=current($t);
		ss_error::log($t,'Callback 1');
			$this->depth += $t == '[' ? 1 : $t == ']' ? -1 : 0; // keep track of bracket depth
	      return ($t == ']' && $this->depth > 0) ? '`]' : // we mark all the inner brackets as skippable
	          ($t[0] == '"' || $t[0] == "'") ? '`' . (array_push($this->str,$t) - 1) :// and replace all the strings
	            $t;
	}
	function query_calback_2($t){
		$t=current($t);
		ss_error::log($t,'Callback 2');
		return $t[0]=='.'?$t:
		($t=='@'?'$obj':
		(preg_match('/:|^(\$|Math|true|false|null)$/',$t)?'':'$obj.').$t);
	}
	
	function query_calback_3($q){
    	@list($t,$a,$b) = $q;
		ss_error::log($t,'Callback 3');
		/*$t=$q[0];
		$a=$q[1];
		$b=(isset($q[2])?$q[2]:'');*/
		preg_match('/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/',$t,$oper);// [?expr] and ?expr and [=expr and =expr
		ss_error::log($oper,'Operator 1');
		if($oper){
			$prefix = '';
			if(preg_match('/^\./',$t)){
				$this->call('expand');
            	$prefix = ",true)";
			}
			$this->call(preg_match('/\=/',$oper[1])?'map':(preg_match('/\^/',$oper[1])?'distinctFilter':'filter'));
			//ss_error::log(preg_match('/\^/',$oper[1])?'map':'false','Post filter');
			return ($prefix . ',function($obj){return ' . $oper[2] . '})');
		}
		preg_match('/^\[\s*([\/\\\\].*)\]/',$t,$oper);
		ss_error::log($oper,'Operator 2');
		if($oper){
			return '.concat().sort(function(a,b){'.preg_replace_callback('/\s*,?\s*([\/\\\\])\s*([^,\\/]+)/',array($this,'query_calback_31'),$oper[1])."})";
		}
		preg_match('/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/',$t,$oper);
		ss_error::log($oper,'Operator 3');
        if($oper){
          $this->call("slice");
          return "," . ($oper[1] || 0) . "," . ($oper[2] || 0) . "," . ($oper[3] || 1) . ")";
        }
		if(preg_match('/^\.\.|\.\*|\[\s*\*\s*\]|,/',$t)){
			$this->call('expand');
			return ($t[1] == '.' ?
              ",'" . b . "'" : // ..prop
                strpos($t,',')!=false ?
                  "," . t : // [prop1,prop2]
                  "") . ")"; // [*]
		}
		return $t;
	}
	function query_calback_4($q){
		ss_error::log($q[0],'Callback 4');
		return $q[1] == "]" ? "]" : $this->str[$q[1]];
	}
	function query_calback_31($q){
    	@list($t,$a,$b) = $q;
		ss_error::log($q[0],'Callback 3_1');
		/*$t=$q[0];
		$a=$q[1];
		$b=(isset($q[2])?$q[2]:'');*/
		return "var av= ".preg_replace('/\$obj/','b',$b).",bv= " . preg_replace('/\$obj/','b',$b).
			";if(av>bv||bv==null){return " . ($a== "/" ? 1 : -1) .";}\n" .
			"if(bv>av||av==null){return " . ($a== "/" ? -1 : 1) .";}\n";
	}
    /*
    function makeRegex($t,$a,$b,$c,$d){
      // creates a regular expression matcher for when wildcards and ignore case is used
	  return $this->str[$d].match("/[\*\?]/") || $c == '~' ?
	  	"/^" + preg_replace("/\\\\([btnfr\\\\\"'])|([^\w\*\?])/g",substr($this->str,1,strlen($this->str[$d])-1),"\\\\$1$2")
      return $this->str[$d].match("/[\*\?]/") || $c == '~' ?
          "/^" + $this->str[$d].substring(1,$this->str[$d].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,".$1") + ($c == '~' ? '$/i' : '$/') + ".test(" + $a + ")" :
          t;
    }
    */
	
}
?>